package total_ws

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"sync"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
	"github.com/rs/xid"
)

// 客户链接消息
type Client struct {
	ID            string          //链接id
	AccountID     string          //账号id
	Socket        *websocket.Conn //链接
	HeartbeatTime int64           //前一次心跳时间
}

// 消息类型
const (
	MessageTypeHeart    = "heartbeat" //心跳
	MessageTypeRegister = "register"  //注册

	HeartbeatCheckTime = 9  //心跳检测几秒一次
	HeartbeatTime      = 20 //心跳距离上一次的最大时间

	ChanBufferRegister   = 100 //注册chan缓冲
	ChanBufferUnregister = 100 // 注销chan大小
)

// 客户端管理
type ClientManager struct {
	Clients  map[string]*Client  //保存客户的连接
	Accounts map[string][]string //账号与链接的关系, map的key是账号id即：AccountId，这里主要考虑到一个账号多个连接
	mu       *sync.Mutex         //异步同步锁
}

// 定义一个管理者
var Manager = ClientManager{
	Clients:  make(map[string]*Client),  // 参与连接的用户，出于性能的考虑，需要设置最大连接数
	Accounts: make(map[string][]string), // 账号和连接关系
	mu:       new(sync.Mutex),           //锁
}

var (
	RegisterChan   = make(chan *Client, ChanBufferRegister)   //注册管道
	UnRegisterChan = make(chan *Client, ChanBufferUnregister) //注销管道
)

// 封转回复格式
type ServiceMessage struct {
	Type    string                `json:"type"` //类型
	Content ServiceMessageContent `json:"content"`
}

type ServiceMessageContent struct {
	Body     string `json:"body"`      // 主要数据
	MetaData string `json:"meta_data"` // 扩展数据
}

func CreateReplyMsg(t string, content ServiceMessageContent) []byte {
	replyMsg := ServiceMessage{
		Type:    t,
		Content: content,
	}
	msg, _ := json.Marshal(replyMsg)
	return msg
}

// 管理链接
// 注册与注销
func register() {
	for {
		select {
		case conn := <-RegisterChan: //监听是否有注册发生
			// 加入链接
			accountBind(conn)

			// 回复消息
			content := CreateReplyMsg(MessageTypeRegister, ServiceMessageContent{})
			_ = conn.Socket.WriteMessage(websocket.TextMessage, content) //注册成功
		case conn := <-UnRegisterChan:

			_ = conn.Socket.Close() //关闭连接

			unAccountBind(conn) //注销账号

		}
	}
}

// 绑定账号
func accountBind(c *Client) {
	Manager.mu.Lock()         //上锁避免重复注册
	defer Manager.mu.Unlock() //错误就解锁 避免死锁

	// 加入新链接
	Manager.Clients[c.ID] = c

	// 加入当绑定
	if _, ok := Manager.Accounts[c.AccountID]; ok { // 该账号已经有绑定，就追加一个绑定
		Manager.Accounts[c.AccountID] = append(Manager.Accounts[c.AccountID], c.ID)
	} else { // 没有就新增一个账号的绑定切片
		Manager.Accounts[c.AccountID] = []string{c.ID}
	}
}

// 解绑账号
func unAccountBind(c *Client) {
	Manager.mu.Lock()
	defer Manager.mu.Unlock()

	delete(Manager.Clients, c.ID) //删除链接
	c.Socket.Close()

	// 取消绑定
	if len(Manager.Accounts[c.AccountID]) > 0 {
		for k, clientID := range Manager.Accounts[c.AccountID] {
			if clientID == c.ID { //找到绑定的客户端id
				Manager.Accounts[c.AccountID] = append(Manager.Accounts[c.AccountID][:k], Manager.Accounts[c.AccountID][k+1:]...)
			}
		}
	}
}

// 心跳检测
func heartbeat() {
	for {
		// 获取所有的clients
		Manager.mu.Lock()
		clients := make([]*Client, 0)
		for _, c := range Manager.Clients {
			clients = append(clients, c)
		}
		Manager.mu.Unlock()
		for _, c := range clients {
			// 如果超过了最大心跳时间则断开连接
			fmt.Println(time.Now().Unix() - c.HeartbeatTime)
			if time.Now().Unix()-c.HeartbeatTime > HeartbeatTime {
				unAccountBind(c)
			}
		}
		time.Sleep(time.Second * HeartbeatCheckTime) //隔多少秒发送一次心跳
	}
}

// 管理链接
func Start() {
	fmt.Println("开启心跳检测")
	// 检查心跳
	go func() {
		defer func() {
			if r := recover(); r != nil {
				log.Println("心跳错误", r)
			}
		}()

		heartbeat()
	}()
	// 注册注销
	go func() {
		defer func() {
			if r := recover(); r != nil {
				log.Println("注册注销", r)
			}
		}()
		register()
	}()
}

// 收发信息
func GetClient(accountID string) []*Client {
	clients := make([]*Client, 0) //创建客户群
	Manager.mu.Lock()
	defer Manager.mu.Unlock()

	if len(Manager.Accounts[accountID]) > 0 {
		// 检查用户旗下的链接是否存在
		for _, clientID := range Manager.Accounts[accountID] {
			if c, ok := Manager.Clients[clientID]; ok {
				clients = append(clients, c)
			}
		}
	}
	return clients
}

// 读取信息
func (c *Client) Read() {
	defer func() {
		_ = c.Socket.Close()
		unAccountBind(c)
	}()
	for {
		// 读取
		_, body, err := c.Socket.ReadMessage()
		if err != nil {
			break
		}

		var msg struct {
			Type string `json:"type"`
		}
		err = json.Unmarshal(body, &msg)
		if err != nil {
			log.Println(err)
			continue //收不到也不要断开
		}
		// 心跳检测
		if msg.Type == MessageTypeHeart { //维持心跳
			c.HeartbeatTime = time.Now().Unix() //刷新链接时间
			fmt.Println("当前心跳时间")
			// 回复心跳
			replyMsg := CreateReplyMsg(MessageTypeHeart, ServiceMessageContent{})
			err = c.Socket.WriteMessage(websocket.TextMessage, replyMsg)
			if err != nil {
				log.Println(err)
			}
			continue
		}

	}
}

// 发送消息
func Send(accounts []string, message ServiceMessage) (err error) {
	msg, err := json.Marshal(message) //解码
	if err != nil {
		return err
	}
	for _, accountID := range accounts {
		// 读取链接id
		clients := GetClient(accountID)
		for _, c := range clients {
			_ = c.Socket.WriteMessage(websocket.TextMessage, msg)
		}
	}
	return nil
}

type MessageNotifyRequest struct {
	UserId string `form:"user_id"`
}

// 请求调用
func MessageNotify(c *gin.Context) {
	// 获取参数
	var params MessageNotifyRequest
	if err := c.ShouldBindQuery(&params); err != nil {
		log.Println(err)
		return
	}
	// TODO: 鉴权
	// 升级
	conn, err := (&websocket.Upgrader{
		CheckOrigin: func(r *http.Request) bool {
			return true
		},
	}).Upgrade(c.Writer, c.Request, nil)
	if err != nil {
		log.Println(err)
		http.NotFound(c.Writer, c.Request)
		return
	}

	// 创建一个链接
	connID := xid.New().String() //创建全球唯一的id
	var client = &Client{
		AccountID:     params.UserId,
		ID:            connID,
		Socket:        conn,
		HeartbeatTime: time.Now().Unix(),
	}
	RegisterChan <- client //注册链接

	// 读取数据
	go func() {
		defer func() {
			if r := recover(); r != nil {
				log.Printf("MessageNotify read panic: %+v\n", r)
			}
		}()
		client.Read()
	}()
}
